#include "linux-alsa-sound.h"
#include <stdio.h>
#include "wav-file.h"

LinuxAlsaSound::LinuxAlsaSound()
{
    this->pcmHandle = nullptr;
    this->pcmParams = nullptr;
}

// 打开alsa 设备
bool LinuxAlsaSound::open(const string &pcmDevice, snd_pcm_stream_t stream)
{

    int err = snd_pcm_open(&pcmHandle, pcmDevice.c_str(), stream, 0);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_open %s:%s ", pcmDevice.c_str(), snd_strerror(err));
        return false;
    }

    return true;
}

// 设置参数
bool LinuxAlsaSound::setParams(pcm_hw_param_t *pcmParm)
{
    // 1 给参数分配内存
    int err = snd_pcm_hw_params_malloc(&pcmParams);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_malloc :%s ", snd_strerror(err));
        return false;
    }

    // 2参数初始化
    err = snd_pcm_hw_params_any(pcmHandle, pcmParams);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_any  :%s ", snd_strerror(err));
        return false;
    }

    // 3 设置交叉方式
    err = snd_pcm_hw_params_set_access(pcmHandle, pcmParams, pcmParm->access);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_set_access :%s ", snd_strerror(err));
        return false;
    }

    // 4 设置数据格式
    err = snd_pcm_hw_params_set_format(pcmHandle, pcmParams, pcmParm->format);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_set_format  :%s ", snd_strerror(err));
        return false;
    }

    // 5 设置通道数
    err = snd_pcm_hw_params_set_channels(pcmHandle, pcmParams, pcmParm->channels);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_set_channels  :%s ", snd_strerror(err));
        return false;
    }

    // 6 设置采样率 也叫采样频率，是指每秒钟采样次数
    err = snd_pcm_hw_params_set_rate_near(pcmHandle, pcmParams, &pcmParm->sampleRate, NULL);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_set_rate_near  :%s ", snd_strerror(err));
        return false;
    }

    // 7 设置一个周期采样多少祯
    err = snd_pcm_hw_params_set_period_size_near(pcmHandle, pcmParams, &pcmParm->frames, NULL);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_set_period_size_nea :%s ", snd_strerror(err));
        return false;
    }

    // 8 设置 配置硬件参数 写入驱动
    err = snd_pcm_hw_params(pcmHandle, pcmParams);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params  :%s ", snd_strerror(err));
        return false;
    }

    // 记录通道数
    this->channels = pcmParm->channels;
    this->sampleBits = pcmParm->sampleBits;

    return true;
}

// 录制声音
bool LinuxAlsaSound::record(uint32_t mtime, vector<uint8_t> &pcmData)
{
    // 文件size=周期数 *
    // 一个周期=祯*通道*位深/8

    // 采样周期 获取一个周期实际采样多少祯
    snd_pcm_uframes_t peroidFrames;
    int err = snd_pcm_hw_params_get_period_size(pcmParams, &peroidFrames, NULL);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_get_period_size  :%s ", snd_strerror(err));
        return false;
    }

    // 采样周期 一个周期多少祯
    // printf("period frames:%ld \n", peroidFrames);
    // 计算一个周期多少字节 采样周期*通道*位深/8
    uint32_t peroidSizeBytes = peroidFrames * channels * sampleBits / 8;
    // printf("peroid size bytes:%d\n", peroidSizeBytes);

    // 获取一个周期采样多少时间
    uint32_t peroidTime; // 毫秒
    err = snd_pcm_hw_params_get_period_time(pcmParams, &peroidTime, NULL);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_get_period_time  :%s ", snd_strerror(err));
        return false;
    }
    // printf("period Time:%d us\n", peroidTime);
    //  计算录制秒数需要多少个周期
    uint32_t peroidCount = mtime * 1000 / peroidTime;
    // printf("peroidCount:%d \n", peroidCount);
    // 计算指定毫秒录制文件需要的大小 周期数*一个周期的大小
    pcmData.resize(peroidSizeBytes * peroidCount);
    // 这个地方不停地录制
    for (int i = 0; i < peroidCount; i++)
    {
        snd_pcm_sframes_t ret = snd_pcm_readi(pcmHandle,
                                              pcmData.data() + i * peroidSizeBytes,
                                              peroidFrames);
        if (ret != peroidFrames)
        {
            fprintf(stderr, "FAIL to snd_pcm_readi  :%s ", snd_strerror(err));
            return false;
        }
    }
    return true;
}

// 录制的声音存储到文件中去
bool LinuxAlsaSound::recordPCMData(uint32_t time, const string &filename)
{

    vector<uint8_t> pcmData;
    bool ok = record(time * 1000, pcmData);
    if (!ok)
    {
        fprintf(stderr, "FAIL to record  :%s ", strerror(errno));
        return false;
    }

    FILE *fp = fopen(filename.c_str(), "w");
    if (!fp)
    {
        fprintf(stderr, "FAIL to fopen  :%s ", strerror(errno));
        return false;
    }

    int ret = fwrite(pcmData.data(), sizeof(uint8_t), pcmData.size(), fp);
    if (ret != pcmData.size())
    {
        fprintf(stderr, "FAIL to fwrite  :%s ", strerror(errno));
        return false;
    }

    fclose(fp);
    return true;
}

bool LinuxAlsaSound::recoredWAVData(uint32_t time, const string &filename)
{
    vector<uint8_t> pcmData;
    record(time * 1000, pcmData);

    // 采样率 美妙采样的次数
    uint32_t sampleRate;
    // 获取实际的采样率
    int err = snd_pcm_hw_params_get_rate(pcmParams, &sampleRate, NULL);
    if (err)
    {
        fprintf(stderr, "FAIL to snd_pcm_hw_params_get_rate  :%s ", snd_strerror(err));
        return false;
    }
    printf("获取的采样率 snd_pcm_hw_params_get_rate frames:%d \n", sampleRate);
    wavFile file(filename);
    file.setParams(sampleRate, channels, sampleBits);
    file.writeData(pcmData.data(), pcmData.size());

    return false;
}

void LinuxAlsaSound::pause(void)
{
    snd_pcm_drain(pcmHandle);
    return;
}

void LinuxAlsaSound::resume(void)
{
    snd_pcm_prepare(pcmHandle);
}
